import * as THREE from 'three'
import { Strong, gsap } from 'gsap'

export default class Bird {
  constructor(x, y, z) {
    this.rSegments = 4
    this.hSegments = 3
    this.cylRay = 120
    this.bodyBirdInitPositions = []
    this.vAngle = this.hAngle = 0
    this.normalSkin = { r: 255 / 255, g: 222 / 255, b: 121 / 255 }
    this.shySkin = { r: 255 / 255, g: 157 / 255, b: 101 / 255 }
    this.color = { r: this.normalSkin.r, g: this.normalSkin.g, b: this.normalSkin.b }
    this.side = 'left'

    this.shyAngles = { h: 0, v: 0 }
    this.behaviourInterval = null
    this.intervalRunning = false

    // bird three root group
    this.threegroup = new THREE.Group()
    this.init()
  }

  init() {
    // materials
    this.yellowMat = new THREE.MeshLambertMaterial({
      color: 0xffde79
    })
    this.whiteMat = new THREE.MeshLambertMaterial({
      color: 0xffffff
    })
    this.blackMat = new THREE.MeshLambertMaterial({
      color: 0x000000
    })
    this.orangeMat = new THREE.MeshLambertMaterial({
      color: 0xff5535
    })

    // wings
    const wingLeftGroup = new THREE.Group()
    const wingRightGroup = new THREE.Group()

    const wingGeom = new THREE.BoxGeometry(60, 60, 5)

    const wingLeft = new THREE.Mesh(wingGeom, this.yellowMat)
    wingLeftGroup.add(wingLeft)
    wingLeftGroup.position.set(70, 0, 0)
    wingLeftGroup.rotation.y = Math.PI / 2
    wingLeft.rotation.x = -Math.PI / 4

    const wingRight = new THREE.Mesh(wingGeom, this.yellowMat)
    wingRightGroup.add(wingRight)
    wingRightGroup.position.set(-70, 0, 0)
    wingRightGroup.rotation.y = -Math.PI / 2
    wingRight.rotation.x = -Math.PI / 4

    this.threegroup.add(wingLeftGroup)
    this.threegroup.add(wingRightGroup)

    // body

    const bodyGeom = new THREE.CylinderGeometry(40, 70, 200, this.rSegments, this.hSegments)
    this.bodyBird = new THREE.Mesh(bodyGeom, this.yellowMat)
    this.bodyBird.position.y = 70

    this.bodyVerticesLength = (this.rSegments + 1) * this.hSegments
    const positions = this.bodyBird.geometry.getAttribute('position').array
    for (let i = 0; i < this.bodyVerticesLength; i++) {
      const index = i * 3
      this.bodyBirdInitPositions.push({
        x: positions[index],
        y: positions[index + 1],
        z: positions[index + 2]
      })
    }

    this.threegroup.add(this.bodyBird)

    this.face = new THREE.Group()
    this.threegroup.add(this.face)

    // eyes
    const eyeGeom = new THREE.BoxGeometry(60, 60, 10)
    const irisGeom = new THREE.BoxGeometry(10, 10, 10)

    this.leftEye = new THREE.Mesh(eyeGeom, this.whiteMat)
    this.leftEye.position.set(-30, 120, 35)
    this.leftEye.rotation.y = -Math.PI / 4

    this.leftIris = new THREE.Mesh(irisGeom, this.blackMat)
    this.leftIris.position.set(-30, 120, 40)
    this.leftIris.rotation.y = -Math.PI / 4

    this.rightEye = new THREE.Mesh(eyeGeom, this.whiteMat)
    this.rightEye.position.set(30, 120, 35)
    this.rightEye.rotation.y = Math.PI / 4

    this.rightIris = new THREE.Mesh(irisGeom, this.blackMat)
    this.rightIris.position.set(30, 120, 40)
    this.rightIris.rotation.y = Math.PI / 4

    this.face.add(this.rightEye)
    this.face.add(this.rightIris)
    this.face.add(this.leftEye)
    this.face.add(this.leftIris)

    // // beak
    const beakGeom = new THREE.CylinderGeometry(0, 20, 20, 4, 1)
    this.beak = new THREE.Mesh(beakGeom, this.orangeMat)
    this.beak.position.set(0, 70, 65)
    this.beak.rotation.x = Math.PI / 2

    this.face.add(this.beak)

    // feathers
    const featherGeom = new THREE.BoxGeometry(10, 20, 5)

    this.feather1 = new THREE.Mesh(featherGeom, this.yellowMat)
    this.feather1.position.set(0, 185, 55)
    this.feather1.rotation.x = Math.PI / 4
    this.feather1.scale.set(1.5, 1.5, 1)

    this.feather2 = new THREE.Mesh(featherGeom, this.yellowMat)
    this.feather2.position.set(20, 180, 50)
    this.feather2.rotation.x = Math.PI / 4
    this.feather2.rotation.z = -Math.PI / 8

    this.feather3 = new THREE.Mesh(featherGeom, this.yellowMat)
    this.feather3.position.set(-20, 180, 50)
    this.feather3.rotation.x = Math.PI / 4
    this.feather3.rotation.z = Math.PI / 8

    this.face.add(this.feather1)
    this.face.add(this.feather2)
    this.face.add(this.feather3)

    this.threegroup.traverse(function (object) {
      if (object instanceof THREE.Mesh) {
        object.castShadow = true
        object.receiveShadow = true
      }
    })
  }

  look(hAngle, vAngle) {
    this.hAngle = hAngle
    this.vAngle = vAngle

    this.leftIris.position.set(-30 + this.hAngle * 10, 120 - this.vAngle * 30, 40 + this.hAngle * 10)
    this.rightIris.position.set(30 + this.hAngle * 10, 120 - this.vAngle * 30, 40 - this.hAngle * 10)

    this.leftEye.position.y = this.rightEye.position.y = 120 - this.vAngle * 10

    this.beak.position.y = 70 - this.vAngle * 20
    this.beak.rotation.x = Math.PI / 2 + this.vAngle / 3

    this.feather1.rotation.x = Math.PI / 4 + this.vAngle / 2
    this.feather1.position.y = 185 - this.vAngle * 10
    this.feather1.position.z = 55 + this.vAngle * 10

    this.feather2.rotation.x = Math.PI / 4 + this.vAngle / 2
    this.feather2.position.y = 180 - this.vAngle * 10
    this.feather2.position.z = 50 + this.vAngle * 10

    this.feather3.rotation.x = Math.PI / 4 + this.vAngle / 2
    this.feather3.position.y = 180 - this.vAngle * 10
    this.feather3.position.z = 50 + this.vAngle * 10

    const positions = this.bodyBird.geometry.getAttribute('position').array

    for (let i = 0; i < this.bodyVerticesLength; i++) {
      const line = Math.floor(i / (this.rSegments + 1))
      const tvInitPos = this.bodyBirdInitPositions[i]
      let a

      if (line >= this.hSegments - 1) {
        a = 0
      } else {
        a = this.hAngle / (line + 1)
      }

      const tx = tvInitPos.x * Math.cos(a) + tvInitPos.z * Math.sin(a)
      const tz = -tvInitPos.x * Math.sin(a) + tvInitPos.z * Math.cos(a)

      // 更新顶点位置
      const index = i * 3 // 因为每个顶点有3个坐标值(x, y, z)
      positions[index] = tx // x
      positions[index + 1] = tvInitPos.y // y
      positions[index + 2] = tz // z
    }

    // 设置顶点位置数组为 dirty，以便更新顶点缓存
    this.bodyBird.geometry.attributes.position.needsUpdate = true

    // 更新模型旋转
    this.face.rotation.y = this.hAngle
  }

  lookAway(fastMove) {
    const speed = fastMove ? 0.4 : 2
    const ease = fastMove ? Strong.easeOut : Strong.easeInOut
    const delay = fastMove ? 0.2 : 0
    const col = fastMove ? this.shySkin : this.normalSkin
    const tv = ((-1 + Math.random() * 2) * Math.PI) / 3
    const beakScaleX = 0.75 + Math.random() * 0.25
    const beakScaleZ = 0.5 + Math.random() * 0.5
    let th = 0

    if (this.side === 'right') {
      th = ((-1 + Math.random()) * Math.PI) / 4
    } else {
      th = (Math.random() * Math.PI) / 4
    }
    gsap.killTweensOf(this.shyAngles)
    gsap.to(this.shyAngles, speed, { v: tv, h: th, ease, delay })
    gsap.to(this.color, speed, { r: col.r, g: col.g, b: col.b, ease, delay })
    gsap.to(this.beak.scale, speed, { z: beakScaleZ, x: beakScaleX, ease, delay })
  }

  stare() {
    const col = this.normalSkin
    let th = 0
    if (this.side === 'right') {
      th = Math.PI / 3
    } else {
      th = -Math.PI / 3
    }
    gsap.to(this.shyAngles, 2, { v: -0.5, h: th, ease: Strong.easeInOut })
    gsap.to(this.color, 2, { r: col.r, g: col.g, b: col.b, ease: Strong.easeInOut })
    gsap.to(this.beak.scale, 2, { z: 0.8, x: 1.5, ease: Strong.easeInOut })
  }
}
